package com.lmc.gobang.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lmc.gobang.game.*;
import com.lmc.gobang.mapper.UserMapper;
import com.lmc.gobang.model.User;
import com.lmc.gobang.game.GameReadyResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Slf4j
@Component
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private RoomManager roomManager;
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        log.info("[GameAPI]Connected to websocket");
        GameReadyResponse gameReadyResponse = new GameReadyResponse();
        //1.获取用户身份（从session中获取）
        User user = (User) session.getAttributes().get("user");
        if(user == null) {
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户尚未登录，未获取到用户信息");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        //2.检查用户是否已进入房间
        Room room = roomManager.getRoomByPlayerId(user.getUserId());
        if(room == null) {
            //如果为空，说明该玩家还没有匹配到
            gameReadyResponse.setOk(false);
            gameReadyResponse.setReason("用户尚未匹配到");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        //3.检查有没有多开，有没有从其他地方进入游戏
        if(onlineUserManager.getFromGameHall(user.getUserId()) != null
           || onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            log.info("玩家："+user.getUsername()+"多开");
            gameReadyResponse.setOk(true);
            gameReadyResponse.setMessage("RepeatConnection");
            gameReadyResponse.setReason("禁止多开游戏页面");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
            return;
        }
        // 检查是否已有其他连接存在
        if (onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            // 已存在一个 session，应该先关闭它
            WebSocketSession oldSession = onlineUserManager.getFromGameRoom(user.getUserId());
            if (oldSession != null && oldSession.isOpen()) {
                oldSession.close();
            }
        }

//        onlineUserManager.enterGameRoom(user.getUserId(), session);

        //4.设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(),session);

        //5.把两个玩家添加到房间中
        //  前面匹配的过程是在game_hall.html中进行的，
        //  所以前面匹配到对手后需要经过页面跳转到game_room.html才算正式完成
        //  现在的逻辑是在game_room.html中进行的
        //  执行到这里才算玩家成功跳转
        synchronized (room){
            if(room.getUser1() == null) {
                //说明此时还没有玩家进来，他是第一个玩家
                room.setUser1(user);
                room.setWhiteUser(user.getUserId());
                log.info("第一个玩家"+user.getUsername()+"已准备就绪");
                return;
            }
            if(room.getUser2() == null) {
                //说明此前已有玩家进来，他是第二个
                room.setUser2(user);
                log.info("第二个玩家"+user.getUsername()+"已准备就绪");
                //到这里说明所有玩家都准备就绪
                //提醒玩家一
                notieGameReady(room,room.getUser1(),room.getUser2());
                //提醒玩家二
                notieGameReady(room,room.getUser2(),room.getUser1());
                return;
            }
        }

        //6.防止又有玩家进入房间，理论上是不存在的，但是为了安全性
        gameReadyResponse.setOk(false);
        gameReadyResponse.setReason("当前房间已满");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));

    }

    private void notieGameReady(Room room, User thisPlayer, User thatPlayer) throws IOException {
        GameReadyResponse gameReadyResponse = new GameReadyResponse();
        gameReadyResponse.setOk(true);
        gameReadyResponse.setMessage("gameReady");
        gameReadyResponse.setIsWhite(room.getWhiteUser());
        gameReadyResponse.setThisUserId(thisPlayer.getUserId());
        gameReadyResponse.setThatUserId(thatPlayer.getUserId());
        gameReadyResponse.setRoomId(room.getRoomId());
        gameReadyResponse.setReason("");
        gameReadyResponse.setThatUserName(thatPlayer.getUsername());

        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisPlayer.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameReadyResponse)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        log.info("handleTextMessage");
        //1.获取到用户信息
        User user = (User) session.getAttributes().get("user");
        if(user == null) {
            log.info("当前用户"+user.getUsername()+"尚未登录");
            return;
        }
        //2.获取房间信息
        Room room = roomManager.getRoomByPlayerId(user.getUserId());
        log.info("准备执行putchess");
        room.putChess(message.getPayload());

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user == null) {
            //这里简单点，后面有空再补
            return;
        }
        if(onlineUserManager.getFromGameRoom(user.getUserId()) == session) {
            onlineUserManager.exitGameRoom(user.getUserId());
            onlineUserManager.printGameRoom();
//            log.info("[handleTransportError]用户"+user.getUsername()+"已离开房间");
        }
        log.info("[handleTransportError]用户"+user.getUsername()+"连接异常已离开房间");
        //玩家中途掉线，直接判定对方获胜
        notieThatPlayerWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if(user == null) {
            //这里简单点，后面有空再补
            return;
        }
        WebSocketSession tmpSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(tmpSession == session) {
            onlineUserManager.exitGameRoom(user.getUserId());
            onlineUserManager.printGameRoom();
//            log.info("[afterConnectionClosed]用户"+user.getUsername()+"已离开房间");
        }
        log.info("[afterConnectionClosed]用户"+user.getUsername()+"已离开房间");
        notieThatPlayerWin(user);
    }

    private void notieThatPlayerWin(User user) throws IOException {
        log.info("notieThatPlayerWin");
        //1.获取房间信息
        Room room = roomManager.getRoomByPlayerId(user.getUserId());
        if(room == null) {
            //房间已被删除，无需通知
            log.info("房间已被删除，无需通知");
            return;
        }
        //2.获取对手信息
        User thatPlayer = room.getUser1()==user?room.getUser2():room.getUser1();
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatPlayer.getUserId());
        if(webSocketSession == null) {
            //对手也掉线了，无需通知
            log.info("对手也掉线了，无需通知");
            return;
        }
        //3.构造响应给对手
        GameResponse gameResponse = new GameResponse();
        gameResponse.setMessage("putChess");
        gameResponse.setWinner(thatPlayer.getUserId());
        gameResponse.setUserId(thatPlayer.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(gameResponse)));
        //4.游戏结束，销毁房间
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
        //5.更新用户信息
        int winnerId = gameResponse.getWinner();
        int loserId  = gameResponse.getWinner()==room.getUser1().getUserId()?room.getUser2().getUserId():room.getUser1().getUserId();
        userMapper.userWin(winnerId);
        userMapper.userLose(loserId);
    }
}
